package com.frameDemo.socket;

import com.frameDemo.frame.Frame;
import com.frameDemo.frame.FrameData;
import com.frameDemo.frame.FrameHead;
import com.frameDemo.queue.FrameQueue;
import com.frameDemo.utils.FrameCheckSum;
import com.frameDemo.utils.FrameProcess;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by TianyuanPan on 10/16/16.
 */
public class SokectDemoServer {


    private ServerSocket serverSocket;
    private Socket socket;
    private InetAddress address;
    private int port;
    private ExecutorService executeService;
    private FrameQueue frameQueue;

    private SokectDemoServer(InetAddress address, int port, int poolSize, FrameQueue frameQueue) {

        this.address = address;
        this.port = port;
        this.frameQueue = frameQueue;
        executeService = Executors.newFixedThreadPool(poolSize);
    }


    public static SokectDemoServer getSchedulerServerSocket(InetAddress address, int port, int poolSize, FrameQueue frameQueue) {

        return new SokectDemoServer(address, port, poolSize, frameQueue);
    }


    public void serverStart() {

        try {

            serverSocket = new ServerSocket(port, 10, address);
            System.out.println("SocketServer have been started." + serverSocket);


            while (true) {

                socket = serverSocket.accept(); // this accept is blocking.
                executeService.submit(new Runnable() {
                    public void run() {

                        byte[] headFalgA;
                        byte[] headFalgB;
                        byte[] headData;

                        InputStream inStream = null;
                        OutputStream outStrem = null;
                        try {
                            inStream = socket.getInputStream();
                            outStrem = socket.getOutputStream();
                            headFalgA = new byte[1];
                            headFalgB = new byte[1];
                            headData = new byte[10];
                            System.out.println("=============> 检测数据帧头部。");
                            inStream.read(headFalgA, 0, 1);

                            if (headFalgA[0] == FrameHead.startFlagA) {

                                System.out.printf("head FLAG_1 is: {0x%x}  ===> OK. continue.\n", headFalgA[0]);

                                inStream.read(headFalgB, 0, 1);

                                if (headFalgB[0] == FrameHead.startFlagB) {

                                    System.out.printf("head FLAG_2 is: {0x%x}  ===> OK. continue.\n", headFalgB[0]);
                                    System.out.println("读取帧首部的信息。");
                                    inStream.read(headData, 0, 10);

                                    byte[] head = {headFalgA[0], headFalgB[0], headData[0], headData[1], headData[2],
                                            headData[3], headData[4], headData[5], headData[6], headData[7], headData[8], headData[9]};

                                    System.out.println("得到的帧头数据:\n\t");
                                    for (byte b : head)
                                        System.out.printf("0x%x ", b);
                                    System.out.println("\n");

                                    FrameHead frameHead = FrameHead.initFrameHead(head);

                                    System.out.println("=== 在这里检查帧首部的检验和。");

                                    if (!FrameCheckSum.isFrameHeadCheckSumOK(frameHead)) {

                                        System.out.printf("Frame head check sum error, return.");
                                        outStrem.write("帧首部检验和出错.".getBytes());
                                        return;
                                    }
                                    System.out.printf("Frame head check sum OK.");

                                    System.out.println("=== 在这里检查帧首部的 VER ID LEN 的合法性，假设合法.");
                                    System.out.println("=== 读取数据帧.");

                                    byte[] dataBody = new byte[frameHead.getDataLen()];
                                    byte[] dataCache = new byte[frameHead.getDataLen()];


                                    int size = 0;
                                    int i = 0;

                                    while ((size = inStream.read(dataCache, 0, frameHead.getDataLen() - 12 - size)) > 0) {

                                        for (int j = 0; j < size; j++) {
                                            dataBody[i + j] = dataCache[j];
                                        }
                                        i += size;

                                        if (i >= frameHead.getDataLen())
                                            break;
                                    }

                                    System.out.println("得到的 数据帧 数据:\n\t");
                                    for (byte b : dataBody)
                                        System.out.printf("0x%x ", b);
                                    System.out.println("\n");

                                    FrameData frameData = FrameData.initFrameData(dataBody);

                                    Frame frame = Frame.initFrame(frameHead, frameData);

                                    System.out.println("=== 在这里检查 数据帧 的检验和。");
                                    if (!FrameCheckSum.isFrameDataCheckSumOK(frame)) {

                                        System.out.println("Frame data check sum error, return.");
                                    }
                                    System.out.println("Frame data check sum OK.");

                                    System.out.println("=== 生成数据帧，加入帧队列，进行相关处理。");
                                    frameQueue.addFrame(new Date().getTime(), frame);

                                    System.out.println("===> 在这里获取相关帧的处理结果，并给客户端发送确认命令。");

                                    String s = FrameProcess.getProcessResult();

                                    outStrem.write(s.getBytes(), 0, s.getBytes().length);


                                } else {

                                    System.out.printf("head FLAF_2 is: {0x%x} NOT {0x%x} ===> not the frame head Flag, return.\n", headFalgB[0], FrameHead.startFlagB);
                                    System.out.println("标志 2 错误.");
                                    //outStrem.write("标志 2 错误.".getBytes());
                                    return;
                                }

                            } else {

                                System.out.printf("head FLAF_1 is: {0x%x} NOT {0x%x} ===> not the frame head Flag, return.\n", headFalgA[0], FrameHead.startFlagA);
                                System.out.println("标志 1 错误.");
                                //outStrem.write("标志 1 错误.".getBytes());
                                return;

                            }

                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {

                            try {
                                inStream.close();
                                outStrem.close();
                                socket.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                    }
                });
            }


        } catch (Exception ex) {

            ex.printStackTrace();

        } finally {

            try {
                if (null != serverSocket) {
                    serverSocket.close();
                    System.out.println("serverSocket close");
                }

            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

    }

}
